#include <stdio.h>
#include <sys/types.h>       
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <net/if.h>
#include <sys/ioctl.h>

#include "bloom_filter.h"

#define TYPECAST_LBF_PORT 2004
#define MAX_BUFFER_SIZE 1024

static void updateLBF(u_int32_t grpaddr, char * filter);

main(int argc, char **argv) 
{ 
  int sid; 
  in_addr_t group_addr; 
  int port; 
  
  ssize_t msglen; 
  struct sockaddr_in src_addr; 
  socklen_t addrlen; 
  char buf[10240]; 
  int * seqno_ptr = buf + sizeof (bloom_filter_t); 
  struct ifreq ifreq; 


  static struct ip_mreq mreq; 
  u_char loop = 1;
  u_char ttl = 64;
	  

  struct sockaddr_in serverName = { 0 },
        clientName = { 0 };



  if (argc <= 3) { 
    printf("%s multicast_group port LBF\n", argv[0]); 
    exit(1); 
  }
  group_addr  =  inet_addr(argv[1]);
  port = atoi(argv[2]); 
  mreq.imr_multiaddr.s_addr = group_addr; 
  mreq.imr_interface.s_addr = INADDR_ANY; 

  sid = socket(PF_INET, SOCK_DGRAM, 0); 
  if (sid < 0) { 
    perror("Error when creating socket"); 
    exit(1); 
  }

  strncpy(ifreq.ifr_name, "eth1", sizeof("eth1")); 

  if (ioctl(sid, SIOCGIFADDR, &ifreq) < 0) { 
    perror("Error when calling ioctl"); 
    exit(1); 
  }
  memcpy(&mreq.imr_interface, &((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr, 
				sizeof(struct in_addr));

  setsockopt(sid, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop));

  setsockopt(socket, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
  

  printf("Joining group %s\n", inet_ntoa(mreq.imr_multiaddr)); 

  if (setsockopt(sid, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { 
    perror("Error when setsockopt"); 
    exit(1); 
  }
  
  updateLBF(group_addr, argv[3]); 
  
  memset(&serverName, 0, sizeof(serverName));
  memset(&clientName, 0, sizeof(clientName));

  serverName.sin_family = AF_INET;
  serverName.sin_addr.s_addr = 
		htonl(INADDR_ANY);
  serverName.sin_port = htons(port);

  if (bind(sid, (struct sockaddr *) 
	   &serverName, sizeof(serverName)) < 0) { 
        perror("bind()");
        exit(1);
  }



  while(1) { 
    msglen =  recvfrom(sid, buf,  sizeof(buf), 0, &src_addr, &addrlen);
    if (msglen < 0) { 
      perror("Error when recvfrom"); 
      continue; 
    }
    
    printf("Received packet from %s (seqno=%d): length is %d\n", inet_ntoa(src_addr.sin_addr) , ntohl(*seqno_ptr), (int)msglen ); 
  }
  
  


}

static void updateLBF(u_int32_t grpaddr, char * filter)
{ 

    int sockfd;
    u_int32_t local_ip_addr = get_local_ip_address(); 
    char buf[1024];
    u_int32_t * grpaddr_ptr;  
    
    struct sockaddr_in dst_addr; 
    memset(&dst_addr, 0, sizeof(struct sockaddr_in));
    dst_addr.sin_family = AF_INET; 
    dst_addr.sin_port = htons(TYPECAST_LBF_PORT); 
    dst_addr.sin_addr.s_addr = htonl(local_ip_addr); 

    bloom_filter_t LBF;
    hex_string_to_bloom_filter(filter, LBF);

    buf[0] = 1; // number of groups
    grpaddr_ptr = (u_int32_t *)(&buf[1]);  // group address
    *grpaddr_ptr = grpaddr; 
    copy_bloom_filter(&buf[1 + sizeof(u_int32_t)], LBF);

    sockfd = socket(PF_INET, SOCK_DGRAM, 0);
    int msglen = sendto(sockfd, buf, sizeof(bloom_filter_t) + sizeof(u_int32_t) + 1, 0, (struct sockaddr *) &dst_addr, sizeof(dst_addr) );
    if (msglen < 0)
    {
          perror("Error sendto\n");
    }    

}
